/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.web.pageflow.metadata.service;

import com.inspur.edp.lcm.metadata.api.entity.*;
import com.inspur.edp.lcm.metadata.api.service.MetadataProjectService;
import com.inspur.edp.lcm.metadata.api.service.MetadataService;
import com.inspur.edp.lcm.metadata.spi.event.MetadataEventArgs;
import com.inspur.edp.web.appconfig.api.entity.GspAppConfig;
import com.inspur.edp.web.appconfig.core.service.GspAppConfigService;
import com.inspur.edp.web.common.entity.TerminalType;
import com.inspur.edp.web.common.io.FileUtility;
import com.inspur.edp.web.common.logger.WebLogger;
import com.inspur.edp.web.common.metadata.GspProjectUtility;
import com.inspur.edp.web.common.metadata.MetadataUtility;
import com.inspur.edp.web.common.utility.StringUtility;
import com.inspur.edp.web.pageflow.metadata.entity.Page;
import com.inspur.edp.web.pageflow.metadata.entity.PageFlowMetadataEntity;
import com.inspur.edp.web.pageflow.metadata.entity.Project;
import io.iec.edp.caf.commons.utils.SpringBeanUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 元数据事件扩展的处理类。
 * 用于当元数据扩展事件被触发时对页面流元数据进行更新。
 * 从Listener里拆出
 */
public class PageFlowMetadataUpdateService {
    private static PageFlowMetadataUpdateService instance;

    public static PageFlowMetadataUpdateService getInstance() {
        if (instance == null) {
            instance = new PageFlowMetadataUpdateService();
        }
        return instance;
    }

    /**
     * 自动更新页面流文件
     *
     * @param metadataEventArgs
     */
    public void updatePageFlowMetadata(MetadataEventArgs metadataEventArgs) {
        if (!metadataEventArgs.getMetadata().getHeader().getType().equals(TerminalType.PC.getMetadataType()) && !metadataEventArgs.getMetadata().getHeader().getType().equals(TerminalType.MOBILE.getMetadataType())) {
            return;
        }

        // (1) 获取当前工程使用的页面流
        GspAppConfig appConfigInfo = getOrCreateGspAppConfig(metadataEventArgs.getMetadata().getRelativePath());
        if (appConfigInfo == null) {
            return;
        }
        PageFlowMetadataEntity pageFlowMetadataEntity = getPageFlowMetadataEntity(metadataEventArgs, appConfigInfo);
        if (pageFlowMetadataEntity == null) {
            return;
        }

        // (2) 更新页面流元数据
        updatePageFlowMetadataEntity(metadataEventArgs, pageFlowMetadataEntity);
        // 更新页面流元数据入口
        if (pageFlowMetadataEntity.getPages().length == 0) {
            pageFlowMetadataEntity.setEntry("");
        } else {
            pageFlowMetadataEntity.setEntry(pageFlowMetadataEntity.getPages()[0].getCode());
        }

        // (3) 更新页面流元数据
        saveRouteMetadataContent(metadataEventArgs, pageFlowMetadataEntity, appConfigInfo);
    }

    /**
     * 删除表单时同步页面流元数据
     *
     * @param metadataEventArgs
     */
    public void syncPageFlowMetadataAfterFormDeleted(MetadataEventArgs metadataEventArgs) {
        WebLogger.Instance.info("Debug_PageFlowMetadataDeleteEventListener_SyncPageFlowMetadata: Sync PageFlow Metadata When Having Deleted Form metadatas.");
        // 监听表单元数据删除。当删除表单元数据，同步页面流元数据
        if (!TerminalType.PC.getMetadataType().equals(metadataEventArgs.getMetadata().getHeader().getType()) && !TerminalType.MOBILE.getMetadataType().equals(metadataEventArgs.getMetadata().getHeader().getType())) {
            return;
        }

        // (1) 获取当前工程使用的页面流
        GspAppConfig appConfigInfo = getGspAppConfigInfoByMetadata(metadataEventArgs.getMetadata().getRelativePath());
        if (appConfigInfo == null) {
            return;
        }
        PageFlowMetadataEntity pageFlowMetadataEntity;

        String projectPath = MetadataUtility.getInstance().getMetadataProjectPath(metadataEventArgs.getMetadata().getRelativePath());
        if (TerminalType.MOBILE.getMetadataType().equals(metadataEventArgs.getMetadata().getHeader().getType())) {
            pageFlowMetadataEntity = RouteMetadataService.getRouteMetadataContentWithMetadataIdAndProjectPath(appConfigInfo.getMobilePageFlowMetadataID(), appConfigInfo.getMobilePageFlowMetadataFileName(), projectPath);
            if (pageFlowMetadataEntity == null) {
                WebLogger.Instance.info(String.format("Current Project %1$s's PageFlowMetadata %2$s Content is Null.", metadataEventArgs.getMetadata().getRelativePath(), appConfigInfo.getPageFlowMetadataFileName()));
                return;
            }
        } else {
            pageFlowMetadataEntity = RouteMetadataService.getRouteMetadataContentWithMetadataIdAndProjectPath(appConfigInfo.getPageFlowMetadataID(), appConfigInfo.getPageFlowMetadataFileName(), projectPath);
            if (pageFlowMetadataEntity == null) {
                WebLogger.Instance.info(String.format("Current Project %1$s's PageFlowMetadata %2$s Content is Null.", metadataEventArgs.getMetadata().getRelativePath(), appConfigInfo.getPageFlowMetadataFileName()));
                return;
            }
        }


        // (2) 从页面流中同步删除该表单元数据的记录
        WebLogger.Instance.info("Current PageFlowMetadataEntity Pages Number is:" + pageFlowMetadataEntity.getPages().length);
        Page[] updatePages = null;
        for (int i = 0; i < pageFlowMetadataEntity.getPages().length; i++) {
            // 检测到该表单元数据存在页面流中
            if (pageFlowMetadataEntity.getPages()[i].getId().equals(metadataEventArgs.getMetadata().getHeader().getId())) {
                ArrayList<Page> tempArrayList = new ArrayList(Arrays.asList(pageFlowMetadataEntity.getPages()));
                tempArrayList.remove(i);
                updatePages = tempArrayList.toArray(new Page[0]);

                break;
            }
        }

        if (updatePages == null) {
            return;
        }
        pageFlowMetadataEntity.setPages(updatePages);
        WebLogger.Instance.info("Updated Pages Number is:" + updatePages.length);

        if (pageFlowMetadataEntity.getPages().length == 0) {
            pageFlowMetadataEntity.setEntry("");
        } else {
            // 设置入口
            pageFlowMetadataEntity.setEntry(pageFlowMetadataEntity.getPages()[0].getCode());
        }

        // (3) 更新页面流元数据
        if (TerminalType.MOBILE.getMetadataType().equals(metadataEventArgs.getMetadata().getHeader().getType())) {
            RouteMetadataService.saveRouteMetadataContent(pageFlowMetadataEntity, appConfigInfo.getMobilePageFlowMetadataID(), metadataEventArgs.getMetadata().getRelativePath());

        } else {
            RouteMetadataService.saveRouteMetadataContent(pageFlowMetadataEntity, appConfigInfo.getPageFlowMetadataID(), metadataEventArgs.getMetadata().getRelativePath());

        }
        WebLogger.Instance.info("Debug_PageFlowMetadataDeleteEventListener_SyncPageFlowMetadata: Done.");
    }

    private GspAppConfig getOrCreateGspAppConfig(String metadataRelativePath) {
        GspAppConfig appConfigInfo = null;
        String currentProjectRelativePath = getProjectRelativePathByMetadata(metadataRelativePath);
        if (StringUtility.isNullOrEmpty(currentProjectRelativePath)) {
            return null;
        }
        appConfigInfo = GspAppConfigService.getCurrent().getOrCreateAppConfig(currentProjectRelativePath);

        return appConfigInfo;
    }

    private GspAppConfig getGspAppConfigInfoByMetadata(String metadataRelativePath) {
        GspAppConfig appConfigInfo = null;
        String currentProjectRelativePath = getProjectRelativePathByMetadata(metadataRelativePath);
        if (StringUtility.isNullOrEmpty(currentProjectRelativePath)) {
            return null;
        }

        appConfigInfo = GspAppConfigService.getCurrent().getGspAppConfigInfo(currentProjectRelativePath);

        return appConfigInfo;
    }

    private String getProjectRelativePathByMetadata(String metadataRelativePath) {
        if (StringUtility.isNullOrEmpty(metadataRelativePath)) {
            return null;
        }

        MetadataProjectService metadataProjectService = SpringBeanUtils.getBean(MetadataProjectService.class);
        MetadataProject metadataProjectInfo = metadataProjectService.getMetadataProjInfo(metadataRelativePath);
        // 返回元数据工程的路径，包含metadata这层路径。工程、元数据工程，mistery。。
        return metadataProjectInfo.getProjectPath();
    }


    private PageFlowMetadataEntity getPageFlowMetadataEntity(MetadataEventArgs metadataEventArgs, GspAppConfig appConfigInfo) {
        PageFlowMetadataEntity pageFlowMetadataEntity = null;

        String pageFlowMetadataId = "";
        String pageFlowMetadataName = "";
        TerminalType terminalType = TerminalType.PC;
        if (TerminalType.PC.getMetadataType().equals(metadataEventArgs.getMetadata().getHeader().getType())) {
            pageFlowMetadataId = appConfigInfo.getPageFlowMetadataID();
            pageFlowMetadataName = appConfigInfo.getPageFlowMetadataFileName();
            terminalType = TerminalType.PC;
        }
        if (TerminalType.MOBILE.getMetadataType().equals(metadataEventArgs.getMetadata().getHeader().getType())) {
            pageFlowMetadataId = appConfigInfo.getMobilePageFlowMetadataID();
            pageFlowMetadataName = appConfigInfo.getMobilePageFlowMetadataFileName();
            terminalType = TerminalType.MOBILE;
        }
        String projectPath = MetadataUtility.getInstance().getMetadataProjectPath(metadataEventArgs.getMetadata().getRelativePath());

        // 表示关联的页面流id为空 可以认为当前不存在对应的页面流
        if (StringUtility.isNullOrEmpty(pageFlowMetadataId)) {
            // 此时需要创建对应的页面流  并写入到appconfig.json 中
            GspMetadata pageFlowMetadata = createRouteMetadataInMemory(projectPath, terminalType);
            PageFlowMetadataEntity createdPageFlowMetadataEntity = (PageFlowMetadataEntity) pageFlowMetadata.getContent();
            if (createdPageFlowMetadataEntity.getPages().length == 0) {
                return null;
            }

            // (2) 持久化页面流元数据
            String routeMetadataRelativePath = PageFlowMetadataUpdateService.getInstance().createRouteMetadataInDisk(pageFlowMetadata);

            // (3) 回写appconfigInfo
            GspAppConfigService.getCurrent().updateAppConfigFile(terminalType, pageFlowMetadata.getHeader().getId(), pageFlowMetadata.getHeader().getFileName(), routeMetadataRelativePath);

            pageFlowMetadataId = pageFlowMetadata.getHeader().getId();
            pageFlowMetadataName = pageFlowMetadata.getHeader().getName();
        }

        pageFlowMetadataEntity = RouteMetadataService.getRouteMetadataContentWithMetadataIdAndProjectPath(pageFlowMetadataId, pageFlowMetadataName, projectPath);

        return pageFlowMetadataEntity;
    }

    private void updatePageFlowMetadataEntity(MetadataEventArgs metadataEventArgs, PageFlowMetadataEntity pageFlowMetadataEntity) {
        boolean existSamePage = checkIfExistsSamePage(metadataEventArgs.getMetadata().getHeader().getId(), pageFlowMetadataEntity);
        if (existSamePage) {
            return;
        }

        Page[] updatePages = null;
        Page page = createPage(metadataEventArgs.getMetadata());
        List<Page> tempArrayList = pageFlowMetadataEntity.getPages() == null ? new ArrayList<>() : new ArrayList<>(Arrays.asList(pageFlowMetadataEntity.getPages()));
        tempArrayList.add(page);
        updatePages = tempArrayList.toArray(new Page[0]);
        pageFlowMetadataEntity.setPages(updatePages);
    }

    private boolean checkIfExistsSamePage(String pageId, PageFlowMetadataEntity pageFlowMetadataEntity) {
        // 默认不存在
        boolean existSamePage = false;

        for (int i = 0; i < pageFlowMetadataEntity.getPages().length; i++) {
            if (pageFlowMetadataEntity.getPages()[i].getId().equals(pageId)) {
                existSamePage = true;
                break;
            }
        }

        return existSamePage;
    }

    private Page createPage(GspMetadata metadata) {
        Page page = new Page();

        page.setId(metadata.getHeader().getId());
        page.setCode(metadata.getHeader().getCode());
        page.setName(metadata.getHeader().getName());
        page.setFileName(metadata.getHeader().getFileName());
        page.setRelativePath(metadata.getRelativePath());
        page.setFormUri(metadata.getHeader().getId());
        page.setRouteUri(metadata.getHeader().getCode());

        return page;
    }

    private void saveRouteMetadataContent(MetadataEventArgs metadataEventArgs, PageFlowMetadataEntity pageFlowMetadataEntity, GspAppConfig appConfigInfo) {
        if (TerminalType.PC.getMetadataType().equals(metadataEventArgs.getMetadata().getHeader().getType())) {
            RouteMetadataService.saveRouteMetadataContent(pageFlowMetadataEntity, appConfigInfo.getPageFlowMetadataID(), metadataEventArgs.getMetadata().getRelativePath());
        } else if (TerminalType.MOBILE.getMetadataType().equals(metadataEventArgs.getMetadata().getHeader().getType())) {
            RouteMetadataService.saveRouteMetadataContent(pageFlowMetadataEntity, appConfigInfo.getMobilePageFlowMetadataID(), metadataEventArgs.getMetadata().getRelativePath());
        }
    }

    /**
     * 创建默认的页面流实体
     * 将当前指定类型的表单填充
     *
     * @param pageFlowMetadataEntityId
     * @param projectPath
     * @param metadataSuffix
     * @return
     */
    public PageFlowMetadataEntity createPageFlowMetadataEntity(String pageFlowMetadataEntityId, String projectPath, String metadataSuffix) {
        PageFlowMetadataEntity localPageFlowMetadataEntity = new PageFlowMetadataEntity();
        // (1) 创建元数据ID
        localPageFlowMetadataEntity.setId(pageFlowMetadataEntityId);
        // (2) 创建project属性
        localPageFlowMetadataEntity.setProject(new Project());
        localPageFlowMetadataEntity.getProject().setName(GspProjectUtility.getProjectName(projectPath));
        // (3) 获取当前工程的所有表单元数据
        List<GspMetadata> formMetataList = MetadataUtility.getInstance().getMetadataListInProjectWithDesign(projectPath, metadataSuffix);
        // (4) 将表单元数据填充到页面流元数据中
        List<Page> pageList = new ArrayList<>();
//        localPageFlowMetadataEntity.setPages((Page[]) java.lang.reflect.Array.newInstance(Page.class, formMetataList.size()));

        for (int i = 0; i < formMetataList.size(); i++) {
            Page page = new Page();
            GspMetadata currentGspMetadata = formMetataList.get(i);
            page.setId(currentGspMetadata.getHeader().getId());
            page.setCode(currentGspMetadata.getHeader().getCode());
            page.setName(currentGspMetadata.getHeader().getName());
            page.setFileName(currentGspMetadata.getHeader().getFileName());

            page.setRelativePath(removeDevRootPathPrefix(currentGspMetadata.getRelativePath()));
            // 设置routeUri
            page.setRouteUri(currentGspMetadata.getHeader().getCode());
            pageList.add(page);
        }

        localPageFlowMetadataEntity.setPages(pageList.stream().toArray(Page[]::new));
        if (localPageFlowMetadataEntity.getPages().length > 0) {
            localPageFlowMetadataEntity.setEntry(localPageFlowMetadataEntity.getPages()[0].getCode());
        }

        return localPageFlowMetadataEntity;
    }

    /**
     * 移除工作空间前缀
     *
     * @param metadataRelativePath
     * @return
     */
    private String removeDevRootPathPrefix(String metadataRelativePath) {
        String devRootPath = MetadataUtility.getInstance().getDevRootPath();
        String independentRelativePath = FileUtility.getPlatformIndependentPath(metadataRelativePath);
        boolean startWithDevRootPrefix = StringUtility.startWith(independentRelativePath, FileUtility.getPlatformIndependentPath(devRootPath));

        // 如果二者有其一是绝对路径 那么不能进行前缀处理
        if (FileUtility.isAbsolute(devRootPath) != FileUtility.isAbsolute(independentRelativePath)) {
            startWithDevRootPrefix = false;
        }

        if (startWithDevRootPrefix) {
            // 如果以工作空间路径开头  那么移除 方便工作空间迁移时可以正常
            independentRelativePath = independentRelativePath.substring(devRootPath.length());
        }
        if (independentRelativePath.startsWith("/")) {
            independentRelativePath = independentRelativePath.substring(1);
        }
        return independentRelativePath;
    }

    public GspMetadata createRouteMetadataInMemory(String projectPath, TerminalType terminalType) {
        // 1. 创建元数据并执行初始化
        GspMetadata routeMetadata = new GspMetadata();
        routeMetadata.setHeader(new MetadataHeader());

        GspProject gspProject = GspProjectUtility.getProjectInformation(projectPath);
        routeMetadata.getHeader().setNameSpace(gspProject.getProjectNameSpace());
        routeMetadata.getHeader().setBizobjectID(gspProject.getBizobjectID());

        updateRouteMetadataHeader(routeMetadata.getHeader(), terminalType);

        String routeMetadataBaseDirectory = "/route";
        routeMetadata.setRelativePath(projectPath + routeMetadataBaseDirectory);

        MetadataService metadataService = SpringBeanUtils.getBean(MetadataService.class);
        metadataService.initializeMetadataEntity(routeMetadata);

        // 2. 创建页面流元数据实体
        PageFlowMetadataEntity localPageFlowMetadataEntity = createPageFlowMetadataEntity(routeMetadata.getHeader().getId(), projectPath, terminalType.getFormMetadataSuffix());

        // 3. 向页面流元数据回填内容
        // 如何写入内容，保证能读取数据
        routeMetadata.setContent(localPageFlowMetadataEntity);

        return routeMetadata;
    }

    public String createRouteMetadataInDisk(GspMetadata routeMetadata) {
        MetadataService metadataService = SpringBeanUtils.getBean(MetadataService.class);
        boolean isPathExists = FileUtility.exists(routeMetadata.getRelativePath());
        if (!isPathExists) {
            FileUtility.createDirectory(routeMetadata.getRelativePath());
        }

        String routeMetadataRelativePath = routeMetadata.getRelativePath();

        routeMetadata.setRelativePath("");
        // 如果页面流文件已经存在 那么删除当前元数据 重新创建 之所以重新创建 是因为routemetadta不一致
        if (metadataService.isMetadataExist(routeMetadataRelativePath, routeMetadata.getHeader().getFileName())) {
            metadataService.deleteMetadata(routeMetadataRelativePath, routeMetadata.getHeader().getFileName());
        }
        metadataService.createMetadata(routeMetadataRelativePath, routeMetadata);
        return routeMetadataRelativePath;
    }

    private void updateRouteMetadataHeader(MetadataHeader routeMetadataHeader, TerminalType terminalType) {
        String routeMetadataCode = terminalType.getRouteMetadata().getCode();
        routeMetadataHeader.setFileName(terminalType.getRouteMetadata().getFileName());
        routeMetadataHeader.setType(terminalType.getRouteMetadata().getType());

        routeMetadataHeader.setCode(routeMetadataCode);
        routeMetadataHeader.setName(routeMetadataCode);
    }
}
